﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ThomasGIS.Coordinates;
using ThomasGIS.Geometries;
using ThomasGIS.Helpers;
using ThomasGIS.SpatialIndex;
using ThomasGIS.Vector;

namespace ThomasGIS.Network
{
    public class GeoDirectedNetwork : GeoNetwork, IGeoNetwork, IDirectedNetwork
    {
        public GeoDirectedNetwork(CoordinateBase coordinate) : base(coordinate)
        {
            
        }

        public GeoDirectedNetwork(IShapefile shapefile, string directionField) : base(shapefile.GetCoordinateRef())
        {
            HashSet<string> pointSet = new HashSet<string>();

            // 从shapefile里拿到所有的道路节点构建顶点集合V
            for (int i = 0; i < shapefile.GetFeatureNumber(); i++)
            {
                Polyline polyline = shapefile.GetFeature(i) as Polyline;

                for (int j = 0; j < polyline.PartNumber; j++)
                {
                    int startLoc = polyline.PartList[j];
                    int endLoc;
                    if (j == polyline.PartNumber - 1)
                    {
                        endLoc = polyline.PointNumber;
                    }
                    else
                    {
                        endLoc = polyline.PartList[j + 1];
                    }

                    for (int k = startLoc; k < endLoc; k++)
                    {
                        pointSet.Add($"{ polyline.PointList[k].X },{ polyline.PointList[k].Y }");
                    }
                }
            }

            List<string> pointStrList = pointSet.ToList();
            pointSet.Clear();

            BoundaryBox mbr = shapefile.GetBoundaryBox();
            double scale = (mbr.XMax - mbr.XMin) / 1000.0;
            pointSpatialIndex = new GridSpatialIndex(mbr, scale);

            // 向网络中添加节点
            foreach (string pointString in pointStrList)
            {
                string[] coordinate = pointString.Split(',');
                double x = Convert.ToDouble(coordinate[0]);
                double y = Convert.ToDouble(coordinate[1]);
                GeoNetworkNode newNode = new GeoNetworkNode(this.NodeNumber, x, y, null);
                this.AddNode(newNode);
                pointSpatialIndex.AddItem(newNode);
            }

            // 刷新空间索引内容
            pointSpatialIndex.RefreshIndex();

            // 构建点与边的关系
            for (int i = 0; i < shapefile.GetFeatureNumber(); i++)
            {
                Polyline polyline = shapefile.GetFeature(i) as Polyline;

                // 方向参数
                int direction = shapefile.GetFieldValueAsInt(i, directionField);

                for (int j = 0; j < polyline.PartNumber; j++)
                {
                    int startLoc = polyline.PartList[j];
                    int endLoc;
                    if (j == polyline.PartNumber - 1)
                    {
                        endLoc = polyline.PointNumber;
                    }
                    else
                    {
                        endLoc = polyline.PartList[j + 1];
                    }

                    for (int k = startLoc; k < endLoc - 1; k++)
                    {
                        Point startPoint = polyline.PointList[k];
                        Point endPoint = polyline.PointList[k + 1];

                        IEnumerable<int> startNodeCandicateIndex = pointSpatialIndex.SearchID(startPoint, scale);
                        IEnumerable<int> endNodeCandicateIndex = pointSpatialIndex.SearchID(endPoint, scale);

                        GeoNetworkNode startNode = null, endNode = null;

                        foreach (int index in startNodeCandicateIndex)
                        {
                            GeoNetworkNode checkNode = this.NodeList[index] as GeoNetworkNode;
                            if (checkNode.X == startPoint.X && checkNode.Y == startPoint.Y)
                            {
                                startNode = checkNode;
                                break;
                            }
                        }

                        foreach (int index in endNodeCandicateIndex)
                        {
                            GeoNetworkNode checkNode = this.NodeList[index] as GeoNetworkNode;
                            if (checkNode.X == endPoint.X && checkNode.Y == endPoint.Y)
                            {
                                endNode = checkNode;
                                break;
                            }
                        }

                        if (startNode == null || endNode == null) throw new Exception("Big Error!");

                        Dictionary<string, object> dataField = new Dictionary<string, object>();
                        dataField.Add("SegmentID", i);

                        if (direction == 1)
                        {
                            this.AddArc(new GeoNetworkArc(startNode, endNode, dataField));
                            this.AddArc(new GeoNetworkArc(endNode, startNode, dataField));
                        }
                        else if (direction == 2)
                        {
                            this.AddArc(new GeoNetworkArc(startNode, endNode, dataField));
                        }
                        else if (direction == 3)
                        {
                            this.AddArc(new GeoNetworkArc(endNode, startNode, dataField));
                        }
                    }
                }
            }
        }

        public int InDegree(int index)
        {
            if (this.neighborMatrix == null || networkChangeFlag == true)
            {
                RefreshNeighborMatrix();
            }

            int inDegree = 0;

            for (int i = 0; i < this.NodeNumber; i++)
            {
                List<LinkValuePair> links = this.neighborMatrix[i];
                for (int j = 0; j < links.Count; j++)
                {
                    if (links[j].EndNodeIndex == index)
                    {
                        inDegree += 1;
                        break;
                    }
                }
            }

            return inDegree;
        }

        public int OutDegree(int index)
        {
            if (this.neighborMatrix == null || networkChangeFlag == true)
            {
                RefreshNeighborMatrix();
            }

            return this.neighborMatrix[index].Count;
        }

        // 有向图的刷新邻接矩阵与无向图不同.jpg
        public override void RefreshNeighborMatrix(string dataField = null)
        {
            if (dataField == null)
            {
                dataField = nowMatrixField;
            }

            RefreshGridSpatialIndex();

            // 有多少个点就有多少条记录，记录内的长度不一致
            this.neighborMatrix = new List<List<LinkValuePair>>();
            // 由于邻接矩阵空间占用较大，在放弃前一个矩阵后应当执行GC过程释放内存
            GC.Collect();

            for (int i = 0; i < NodeNumber; i++)
            {
                this.neighborMatrix.Add(new List<LinkValuePair>());
            }

            for (int i = 0; i < this.ArcList.Count; i++)
            {
                GeoNetworkArc oneArc = this.ArcList[i] as GeoNetworkArc;

                IEnumerable<int> startNodeCandicateIndex = pointSpatialIndex.SearchID(new Point(oneArc.StartPoint.X, oneArc.StartPoint.Y), this.pointSpatialIndex.GetScale());
                IEnumerable<int> endNodeCandicateIndex = pointSpatialIndex.SearchID(new Point(oneArc.EndPoint.X, oneArc.EndPoint.Y), this.pointSpatialIndex.GetScale());

                int startNodeIndex = -1, endNodeIndex = -1;

                foreach (int index in startNodeCandicateIndex)
                {
                    GeoNetworkNode checkNode = this.NodeList[index] as GeoNetworkNode;
                    if (checkNode.X == oneArc.StartPoint.X && checkNode.Y == oneArc.StartPoint.Y)
                    {
                        startNodeIndex = index;
                        break;
                    }
                }

                foreach (int index in endNodeCandicateIndex)
                {
                    GeoNetworkNode checkNode = this.NodeList[index] as GeoNetworkNode;
                    if (checkNode.X == oneArc.EndPoint.X && checkNode.Y == oneArc.EndPoint.Y)
                    {
                        endNodeIndex = index;
                        break;
                    }
                }

                if (startNodeIndex == -1 || endNodeIndex == -1) throw new Exception("Big Error!");

                if (dataField != "")
                {
                    // 如果边的属性表中不包含该字段，则直接跳过
                    if (!oneArc.GetKeys().Contains(dataField)) continue;

                    this.neighborMatrix[startNodeIndex].Add(new LinkValuePair(endNodeIndex, (double)oneArc.GetProperty(dataField), i));
                }
                else
                {
                    double distance;
                    if (networkCoordinateSystem.GetCoordinateType() == CoordinateType.Projected)
                    {
                        distance = DistanceCalculator.SpatialDistance(oneArc.StartPoint, oneArc.EndPoint);
                    }
                    else
                    {
                        distance = DistanceCalculator.SpatialDistanceGeo(oneArc.StartPoint, oneArc.EndPoint);
                    }
                    this.neighborMatrix[startNodeIndex].Add(new LinkValuePair(endNodeIndex, distance, i));
                }
            }

            this.nowMatrixField = dataField;
            this.networkChangeFlag = false;
        }
    }
}
